﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PClusterModel.Infrastructure;
using System.Collections;
using PClusterModel.Utils;
using PClusterModel.Cluster;

namespace PClusterModel.Cluster
{
    public class Cliquer
    {
        private Dictionary<IMicroarrayDataObject, int> cliqueSize;
        private Clique currentClique;
        private Dictionary<IMicroarrayDataObject, Block> blocks;

        public Cliquer(Dictionary<IMicroarrayDataObject, Block> blocks)
        {
            this.blocks = blocks;
            this.cliqueSize = new Dictionary<IMicroarrayDataObject, int>();
        }

        public Clique FindMaximalClique(List<IMicroarrayDataObject> objects, BitArray edge, int minimalNumberOfObjectsInClique)
        {
            this.currentClique = new Clique();
            this.cliqueSize.Clear();

            IMicroarrayDataObject current = objects[0];
            IMicroarrayDataObject previous;

            List<IMicroarrayDataObject> candidateClique = new List<IMicroarrayDataObject>();
            this.cliqueSize[current] = 1;

            int objectsCount = objects.Count;
            for (int i = 1; i < objectsCount; i++)
            {
                previous = current;
                current = objects[i];

                for (int j = 0; j < i; j++)
                {
                    IMicroarrayDataObject nextInClique = objects[j];
                    if (this.AreObjectsConnectedWithConditionSet(current, nextInClique, edge))
                    {
                        candidateClique.Add(nextInClique);
                    }
                }

                if (this.ObjectsArePartOfClique(candidateClique, edge, this.cliqueSize[previous]))
                {
                    this.currentClique.Add(current);
                    this.cliqueSize[current] = this.cliqueSize[previous] + 1;
                }
                else
                {
                    this.cliqueSize[current] = this.cliqueSize[previous];
                }
                candidateClique.Clear();
            }
            if (this.currentClique.Count < minimalNumberOfObjectsInClique)
            {
                return null;
            }
            return currentClique;
        }

        private bool ObjectsArePartOfClique(List<IMicroarrayDataObject> candidateCliqueMembers, BitArray currentEdge, int minSize)
        {
            int countOfCandidate = candidateCliqueMembers.Count;
            if (minSize <= 1)
            {
                if (countOfCandidate > 0 && minSize == 1)
                {
                    currentClique.Clear();
                    currentClique.Add(candidateCliqueMembers[0]);
                    return true;
                }
            }

            if (countOfCandidate < minSize)
            {
                return false;
            }

            List<IMicroarrayDataObject> newCandidates = new List<IMicroarrayDataObject>();
            IMicroarrayDataObject currentCandidate;

            for (int i = countOfCandidate - 1; i >= minSize - 1; i--)
            {
                currentCandidate = candidateCliqueMembers[i];

                if (cliqueSize[currentCandidate] < minSize)
                    break;

                for (int j = 0; j < i; j++)
                {
                    IMicroarrayDataObject newCandidate = candidateCliqueMembers[j];
                    if (this.AreObjectsConnectedWithConditionSet(currentCandidate, newCandidate, currentEdge))
                    {
                        newCandidates.Add(newCandidate);
                    }
                }

                int newCandidatesCount = newCandidates.Count;
                if (newCandidatesCount < minSize - 1)
                {
                    newCandidates.Clear();
                    continue;
                }

                if (cliqueSize[newCandidates[newCandidatesCount - 1]] < minSize - 1)
                {
                    newCandidates.Clear();
                    continue;
                }

                if (this.ObjectsArePartOfClique(newCandidates, currentEdge, minSize - 1))
                {
                    currentClique.Add(currentCandidate);
                    return true;
                }
                else
                {
                    newCandidates.Clear();
                }
            }
            return false;
        }

        private bool AreObjectsConnectedWithConditionSet(IMicroarrayDataObject firsIMicroarrayDataObject, IMicroarrayDataObject secondObject, BitArray conditionSet)
        {
            Block currentBlock = this.blocks[firsIMicroarrayDataObject];
            MaximalDimensionBlockSet mds = currentBlock.GetMaximalDimensionSetWithObject(secondObject);
            if (mds == null)
            {
                return false;
            }
            BitArray comparedBitmap;
            for (int i = 0; i < mds.ConditionBitMaps.Count; i++)
            {
                comparedBitmap = mds.ConditionBitMaps[i];
                if (Utilities.IsBitmapSubsetOfSecondBitmap(conditionSet, comparedBitmap))
                {
                    return true;
                }
            }
            return false;
        }
    }
}
